<!DOCTYPE html>
<!-- The previous line tells the browser, that the page uses the HTML5 standard. -->

<html>
	<head>
		<title>Three.js tutorial - Lesson 04</title>
		<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">

		<!-- The following meta line optimizes the site for mobile devices. It sets the viewport size
		to the screen size, so it will be displayed maximized, but unscaled. -->
		<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1">
		<style type="text/css">
			body {
				/* Set the background color of the HTML page to black */
				background-color: #000000;

				/* Hide oversized content. This prevents the scroll bars. */
				overflow: hidden;
			}
		</style>
		<!-- Include two libraries
		1. Three.js (core library)
		2. Detector.js (checks the browsers WebGL capabilities) -->
		<script src="./three.js"></script>
		<script src="./Detector.js"></script>
	</head>
	<body>
		<!-- This is the DIV element which will contain the WebGL canvas. To be identifiable lateron,
		the id 'WebGLCanvas' is applied to it. -->
		<div id="WebGLCanvas">

		<!-- This JavaScript block encloses the Three.js commands -->
		<script>
			// Global scene object
			var scene;

			// Global camera object
			var camera;

			// Global mesh object of the triangle
			var triangleMesh;

			// Global mesh object of the square
			var squareMesh;

			// Initialize the scene
			initializeScene();

			// Instead of calling 'renderScene()', we call a new function: 'animateScene()'. It will
			// update the rotation values and call 'renderScene()' in a loop.

			// Animate the scene
			animateScene();

			/**
			 * Initialze the scene.
			 */
			function initializeScene(){
				// Check whether the browser supports WebGL. If so, instantiate the hardware accelerated
				// WebGL renderer. For antialiasing, we have to enable it. The canvas renderer uses
				// antialiasing by default.
				// The approach of multiplse renderers is quite nice, because your scene can also be
				// viewed in browsers, which don't support WebGL. The limitations of the canvas renderer
				// in contrast to the WebGL renderer will be explained in the tutorials, when there is a
				// difference.
				if(Detector.webgl){
					renderer = new THREE.WebGLRenderer({antialias:true});

				// If its not supported, instantiate the canvas renderer to support all non WebGL
				// browsers
				} else {
					renderer = new THREE.CanvasRenderer();
				}

				// Set the background color of the renderer to black, with full opacity
				renderer.setClearColor(0x000000, 1);

				// Get the size of the inner window (content area) to create a full size renderer
				canvasWidth = window.innerWidth;
				canvasHeight = window.innerHeight;

				// Set the renderers size to the content areas size
				renderer.setSize(canvasWidth, canvasHeight);

				// Get the DIV element from the HTML document by its ID and append the renderers DOM
				// object to it
				document.getElementById("WebGLCanvas").appendChild(renderer.domElement);

				// Create the scene, in which all objects are stored (e. g. camera, lights,
				// geometries, ...)
				scene = new THREE.Scene();

				// Now that we have a scene, we want to look into it. Therefore we need a camera.
				// Three.js offers three camera types:
				//  - PerspectiveCamera (perspective projection)
				//  - OrthographicCamera (parallel projection)
				//  - CombinedCamera (allows to switch between perspective / parallel projection
				//    during runtime)
				// In this example we create a perspective camera. Parameters for the perspective
				// camera are ...
				// ... field of view (FOV),
				// ... aspect ratio (usually set to the quotient of canvas width to canvas height)
				// ... near and
				// ... far.
				// Near and far define the cliping planes of the view frustum. Three.js provides an
				// example (http://mrdoob.github.com/three.js/examples/
				// -> canvas_camera_orthographic2.html), which allows to play around with these
				// parameters.
				// The camera is moved 10 units towards the z axis to allow looking to the center of
				// the scene.
				// After definition, the camera has to be added to the scene.
				camera = new THREE.PerspectiveCamera(45, canvasWidth / canvasHeight, 1, 100);
				camera.position.set(0, 0, 10);
				camera.lookAt(scene.position);
				scene.add(camera);

				
                // Triangle
                
				var triangleGeometry = new THREE.Geometry();
				triangleGeometry.vertices.push(new THREE.Vector3( 0.0,  1.0, 0.0));
				triangleGeometry.vertices.push(new THREE.Vector3(-1.0, -1.0, 0.0));
				triangleGeometry.vertices.push(new THREE.Vector3( 1.0, -1.0, 0.0));
				triangleGeometry.faces.push(new THREE.Face3(0, 1, 2));

				triangleGeometry.faces[0].vertexColors[0] = new THREE.Color(0xFF0000);
				triangleGeometry.faces[0].vertexColors[1] = new THREE.Color(0x00FF00);
				triangleGeometry.faces[0].vertexColors[2] = new THREE.Color(0x0000FF);

				var triangleMaterial = new THREE.MeshBasicMaterial({
					vertexColors:THREE.VertexColors,
					side:THREE.DoubleSide
				});

				triangleMesh = new THREE.Mesh(triangleGeometry, triangleMaterial);
				triangleMesh.position.set(-3.0, -1.5, -2.0);
				scene.add(triangleMesh);

                // Square
				var squareGeometry = new THREE.Geometry();
				var offset = 0;
				var index = 0;
				for (var i = 0; i < 10; i++){
					
					// Calculating vertices using an offset greater than .05 so the boxes are apart.
					squareGeometry.vertices.push(new THREE.Vector3(-1.0 + offset, 1.0 - offset, 0.0));
					squareGeometry.vertices.push(new THREE.Vector3(-0.95 + offset, 0.95 - offset, 0.0));
					squareGeometry.vertices.push(new THREE.Vector3(1.0 - offset, 1.0 - offset, 0.0));
					squareGeometry.vertices.push(new THREE.Vector3(0.95 - offset, 0.95 - offset, 0.0));
					squareGeometry.vertices.push(new THREE.Vector3(1.0 - offset, -1.0 + offset, 0.0));
					squareGeometry.vertices.push(new THREE.Vector3(0.95 - offset, -0.95 + offset, 0.0));
					squareGeometry.vertices.push(new THREE.Vector3(-1.0 + offset, -1.0 + offset, 0.0));
					squareGeometry.vertices.push(new THREE.Vector3(-0.95 + offset, -0.95 + offset, 0.0));
					
					// Keeps track of number of vertices, which is used to calculate face vertices.
					index += 8;
					
					squareGeometry.faces.push(new THREE.Face3(index - 8, index - 7, index - 6));
					squareGeometry.faces.push(new THREE.Face3(index - 7, index - 6, index - 5));
					squareGeometry.faces.push(new THREE.Face3(index - 6, index - 5, index - 4));
					squareGeometry.faces.push(new THREE.Face3(index - 5, index - 4, index - 3));
					squareGeometry.faces.push(new THREE.Face3(index - 4, index - 3, index - 2));
					squareGeometry.faces.push(new THREE.Face3(index - 3, index - 2, index - 1));
					squareGeometry.faces.push(new THREE.Face3(index - 2, index - 1, index - 8));
					squareGeometry.faces.push(new THREE.Face3(index - 1, index - 8, index - 7));
					
					offset += .1;
					
					// Determine varying color for each box thing.
					for (var j = index - 8; j < index; j++){
						squareGeometry.faces[j].color.setRGB(0, 1 - offset * .75, 0);
					}
				}

				var squareMaterial = new THREE.MeshBasicMaterial({
                    vertexColors:THREE.FaceColors,
					side:THREE.DoubleSide
				});

				squareMesh = new THREE.Mesh(squareGeometry, squareMaterial);
				squareMesh.position.set(0.0, 0.0, 0.0);
				scene.add(squareMesh);
                
                // Circle
				
                var circleGeometry = new THREE.Geometry();
                
				// Push the center vertex
                circleGeometry.vertices.push(new THREE.Vector3(0.0, 0.0, 0.0));
				var degree = 0.0;
                const toRadians = Math.PI/180;
				
                while (degree < 360){
					// Push v(i)
                    circleGeometry.vertices.push(new THREE.Vector3(Math.cos(2.0 * degree * toRadians), Math.sin(2.0 * degree * toRadians), 0.0));
                    
					// Push v(i + 1)
					degree++;
					circleGeometry.vertices.push(new THREE.Vector3(Math.cos(2.0 * degree * toRadians), Math.sin(2.0 * degree * toRadians), 0.0));
                    
					circleGeometry.faces.push(new THREE.Face3(0, degree, degree + 1));
					
					// Since the top is supposed to be shaded and bottom is supposed to be bright red, the color
					// should depend on sin(degree). Since sin(90) = 1 = bright red, we need to invert the sin
					// so that the shaded part is on the top. Also, since max R value is 1, we need to make the 
					// range of the calculated R value go from [0, 1];
					circleGeometry.faces[degree - 1].color.setRGB((-Math.sin(degree * toRadians) + 1) / 2, 0, 0);
                }
                
                var circleMaterial = new THREE.MeshBasicMaterial({
                    vertexColors:THREE.FaceColors,
                    side:THREE.DoubleSide
                });
                
                circleMesh = new THREE.Mesh(circleGeometry, circleMaterial);
                circleMesh.position.set(2.0, 2.0, 0.0);
                scene.add(circleMesh);
			}

			/**
			 * Animate the scene and call rendering.
			 */
			function animateScene(){
				// At first, we increase the y rotation of the triangle mesh and decrease the x
				// rotation of the square mesh.
				// Increase the y rotation of the triangle
				triangleMesh.rotation.y += 0.05
				triangleMesh.rotation.x += 0.01

				// Decrease the x rotation of the square
				squareMesh.rotation.x -= 0.01;
				squareMesh.rotation.z -= 0.1;
				
				circleMesh.rotation.z += 0.2

				// Define the function, which is called by the browser supported timer loop. If the
				// browser tab is not visible, the animation is paused. So 'animateScene()' is called
				// in a browser controlled loop.
				requestAnimationFrame(animateScene);

				// Map the 3D scene down to the 2D screen (render the frame)
				renderScene();
			}

			/**
			 * Render the scene. Map the 3D world to the 2D screen.
			 */
			function renderScene(){
				renderer.render(scene, camera);
			}
		</script>
	</body>
</html>